package com.jinhua.batch.service;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.util.CollectionUtils;
import com.alibaba.excel.util.FileUtils;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.google.common.collect.Lists;
import com.jinhua.batch.constant.StageEnum;
import com.jinhua.batch.exception.ResplitException;
import com.jinhua.batch.model.BatchCallbackHandler;
import com.jinhua.batch.model.BatchProcessRequest;
import com.jinhua.batch.model.ProgressVO;
import com.jinhua.batch.model.ResultItemVO;
import com.jinhua.batch.utils.ProgressUtil;
import com.jinhua.common.utils.JsonUtils;
import com.jinhua.common.utils.ScaffoldingTsfUtils;
import com.jinhua.cos.ScaffoldingCosSdkService;
import com.jinhua.exceptions.TRetailRuntimeException;
//import com.tencent.sr.rmall.springbootstarter.route.TsfTagUtils;
import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import org.apache.commons.lang3.exception.ExceptionUtils;
//import org.jetbrains.annotations.NotNull;
//import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/**
 * Copyright
 *
 * @Author WuHuang
 * @Date 2023/3/31
 */
@Component
public class DataHandleService {
    private static final Logger log = LoggerFactory.getLogger(DataHandleService.class);
    @Resource
    private ScaffoldingCosSdkService scaffoldingCosSdkService;

    public DataHandleService() {
    }

    public <T extends ResultItemVO> void handleDataInExcel(BatchProcessRequest<T> batchProcessRequest, Class<T> lineClass, List<T> parseResultList, String taskId, String rawUrl, String exportBucket) {
        if (CollectionUtils.isEmpty(parseResultList)) {
            throw new TRetailRuntimeException("上传的excel的内容为空，请检查后重新上传。");
        } else {
            this.reportHandlingStatus(taskId, rawUrl);
            batchProcessRequest.getBatchCallbackHandler().beforeHandle(parseResultList);
            List<List<T>> splitParseResultList = this.splitResultList(batchProcessRequest, parseResultList, taskId, rawUrl);
            log.info("数据切分完成，splitParseResultList={}", JsonUtils.toString(splitParseResultList));
            List<T> allResultItemVOList = null;
            if (!CollectionUtils.isEmpty(splitParseResultList)) {
                allResultItemVOList = this.invokeItemBatchCallbackHandler(batchProcessRequest, parseResultList, splitParseResultList);
            }

            this.invokeFinishCallbackHandler(batchProcessRequest, allResultItemVOList);
            this.uploadHandleResultAndMarkFinish(lineClass, parseResultList, taskId, rawUrl, exportBucket, allResultItemVOList);
        }
    }

    private ProgressVO reportHandlingStatus(String taskId, String rawUrl) {
        ProgressVO progressVO = new ProgressVO();
        progressVO.setTaskId(taskId);
        progressVO.setCurrentStage(StageEnum.SAVING_PROCESS_RESULT.getDesc());
        progressVO.setUploadFileUrl(rawUrl);
        ProgressUtil.saveProgress(progressVO);
        return progressVO;
    }

    private <T extends ResultItemVO> List<List<T>> splitResultList(BatchProcessRequest<T> batchProcessRequest, List<T> parseResultList, String taskId, String rawUrl) {
        ProgressVO progressVO = new ProgressVO();
        if (CollectionUtils.isEmpty(parseResultList)) {
            progressVO.setTaskId(taskId);
            progressVO.setCurrentStage(StageEnum.FINISH.getDesc());
            progressVO.setUploadFileUrl(rawUrl);
            ProgressUtil.saveProgress(progressVO);
            return null;
        } else {
            List<T> copyOfParseResultList = new ArrayList(parseResultList);
            List<List<T>> splitedParseResultList = Lists.partition(copyOfParseResultList, batchProcessRequest.getSplitSize());
            return splitedParseResultList;
        }
    }

    private <T extends ResultItemVO> void invokeFinishCallbackHandler(BatchProcessRequest<T> batchProcessRequest, List<T> allResultItemVOList) {
        log.info("开始调用后置处理点, batchProcessRequest={}", JsonUtils.toString(batchProcessRequest));
        BatchCallbackHandler batchCallbackHandler = batchProcessRequest.getBatchCallbackHandler();
        if (null != batchCallbackHandler) {
            batchCallbackHandler.afterHandle(allResultItemVOList);
            log.info("调用后置处理点成功, batchProcessRequest={}", JsonUtils.toString(batchProcessRequest));
        }
    }

    private <T extends ResultItemVO> List<T> invokeItemBatchCallbackHandler(BatchProcessRequest<T> batchProcessRequest, List<T> parseResultList, List<List<T>> splitedParseResultListOfParam) {
        List<T> allResultItemVOList = new ArrayList();
        BatchCallbackHandler<T> batchCallbackHandler = batchProcessRequest.getBatchCallbackHandler();
        if (null == batchCallbackHandler) {
            throw new TRetailRuntimeException("INTERNAL_ERROR", "processFinishCallbackHandler is null");
        } else {
            log.info("准备开始批量调用回调函数，batchProcessRequest={}", JsonUtils.toString(batchProcessRequest));
            ProgressVO progressVO = new ProgressVO();
            progressVO.setTaskId(batchProcessRequest.getTaskId());
            progressVO.setCurrentStage(StageEnum.BATCH_PROCESSING.getDesc());
            progressVO.setUploadFileUrl(batchProcessRequest.getExcelUrl());
            progressVO.setTotalCount(parseResultList.size());
            ProgressUtil.saveProgress(progressVO);
            int successCount = 0;
            int failCount = 0;
            List<List<T>> allResplitListList = new ArrayList();
            List<List<T>> toHandleListList = new ArrayList();
            toHandleListList.addAll(splitedParseResultListOfParam);

            label54:
            while(!CollectionUtils.isEmpty(toHandleListList)) {
                Iterator<List<T>> listIterator = toHandleListList.iterator();
                int i = 0;

                while(true) {
                    List<T> resultItemVOList;
                    int itemSize;
                    int itemSuccessSize;
                    while(true) {
                        if (!listIterator.hasNext()) {
                            toHandleListList.clear();
                            toHandleListList.addAll(allResplitListList);
                            allResplitListList.clear();
                            continue label54;
                        }

                        List<T> itemParseResultList = (List)listIterator.next();
                        resultItemVOList = null;

//                        String dataRouteTag = TsfTagUtils.getDataRouteTag();
//                        String tsrLaneId = TsfTagUtils.getTsrLaneId();
//                        String tversion = ScaffoldingTsfUtils.getTversion();
//                        TsfTagUtils.removeLaneContext();
//                        TsfTagUtils.setLaneId(tsrLaneId, dataRouteTag);
//                        ScaffoldingTsfUtils.setTversion(tversion);

                        itemSize = itemParseResultList.size();

                        try {
                            resultItemVOList = this.invokeCallbackHandlerSeveralTime(batchCallbackHandler, itemParseResultList, batchProcessRequest.getFailRetryTime(), i);
                            break;
                        } catch (ResplitException var22) {
                            listIterator.remove();
                            itemSuccessSize = itemParseResultList.size() > 1 ? itemParseResultList.size() / 2 : 1;
                            List<List<T>> resplitListList = Lists.partition(itemParseResultList, itemSuccessSize);
                            allResplitListList.addAll(resplitListList);
                            log.info("产生裂变！itemParseResultList=" + JsonUtils.toString(itemParseResultList));
                        }
                    }

                    if (!CollectionUtils.isEmpty(resultItemVOList)) {
                        allResultItemVOList.addAll(resultItemVOList);
                    }

                    int itemFailedCount = 0;

                    if (resultItemVOList != null) {
                        List<T> failItemVOList = resultItemVOList.stream()
                                .filter(resultItemVO -> !resultItemVO.isSuccess())
                                .collect(Collectors.toList());
                        itemFailedCount = failItemVOList.size();
                    }


                    itemSuccessSize = itemSize - itemFailedCount;
                    if (itemSuccessSize < 0) {
                        itemSuccessSize = 0;
                    }

                    successCount += itemSuccessSize;
                    failCount += itemFailedCount;
                    progressVO = ProgressUtil.queryTaskStatus(batchProcessRequest.getTaskId());
                    progressVO.setFailCount(failCount);
                    progressVO.setSuccessCount(successCount);
                    ProgressUtil.saveProgress(progressVO);
                    ++i;
                }
            }

            log.info("批量调用回调函数完成，batchProcessRequest={}，allResultItemVOList={}", JsonUtils.toString(batchProcessRequest), JsonUtils.toString(allResultItemVOList));
            return allResultItemVOList;
        }
    }

    private <T extends ResultItemVO> List<T> invokeCallbackHandlerSeveralTime(BatchCallbackHandler<T> batchCallbackHandler, List<T> itemParseResultList, int failRetryTime, int batchNo) throws ResplitException {
        List<T> resultItemVOList = null;
        List<T> copyOfItemParseResultList = new ArrayList(itemParseResultList);
        int i = 0;

        while(true) {
            if (i < failRetryTime) {
                try {
                    resultItemVOList = batchCallbackHandler.itemBatchHandle(itemParseResultList, i + 1);
                } catch (Exception var12) {
                    Exception e = var12;
                    resultItemVOList = new ArrayList();
                    Iterator var9 = copyOfItemParseResultList.iterator();

                    while(var9.hasNext()) {
                        ResultItemVO item = (ResultItemVO)var9.next();
                        item.setSuccess(false);
                        item.isSuccess();
                        item.setFailReason(ExceptionUtils.getStackTrace(e));
                        ((List)resultItemVOList).add(item);
                    }
                }

                log.info("第" + batchNo + "批数据第" + i + "次处理完成，对应的处理失败的数据列表是" + JsonUtils.toString(resultItemVOList));
                List<T> failItemVOList = null;
                if (resultItemVOList != null) {
                    failItemVOList = resultItemVOList.stream()
                            .filter(resultItemVO -> !resultItemVO.isSuccess())
                            .collect(Collectors.toList());
                }


                if (failRetryTime != i + 1 && !CollectionUtils.isEmpty(failItemVOList)) {
                    log.info("第" + batchNo + "批数据处理有问题，继续重试");
                    ++i;
                    continue;
                }

                return (List)resultItemVOList;
            }

            return (List)resultItemVOList;
        }
    }

    private <T> void uploadHandleResultAndMarkFinish(Class<T> lineClass, List<T> parseResultList, String taskId, String rawUrl, String exportBucket, List<T> allResultItemVOList) {
        ProgressVO progressVO = new ProgressVO();
        if (CollectionUtils.isEmpty(allResultItemVOList)) {
            progressVO.setTaskId(taskId);
            progressVO = ProgressUtil.queryTaskStatus(taskId);
            progressVO.setCurrentStage(StageEnum.FINISH.getDesc());
            ProgressUtil.saveProgress(progressVO);
        } else {
            String resultFileName = "excel-batch-process-result-" + taskId + ".xlsx";
            File resultExcelFile = FileUtils.createTmpFile(resultFileName);
            ExcelWriter resultExcelWriter = EasyExcel.write(resultExcelFile, lineClass).build();
            WriteSheet resultSheetWriter = EasyExcel.writerSheet("处理结果").build();
            resultExcelWriter.write(allResultItemVOList, resultSheetWriter);
            log.info("开始上传处理结果的excel到cos, resultFileName={}, allResultItemVOList={}", resultFileName, JsonUtils.toString(allResultItemVOList));
            resultExcelWriter.finish();
            String resultUrl = this.scaffoldingCosSdkService.uploadFile(exportBucket, String.format("/excelBatchProcess/importFailed/%s/%s", taskId, resultFileName), resultExcelFile);
            log.info("上传处理结果的excel到cos成功 ,resultFileName={}, allResultItemVOList={}", resultFileName, JsonUtils.toString(allResultItemVOList));
            FileUtils.delete(resultExcelFile);
            progressVO = ProgressUtil.queryTaskStatus(taskId);
            progressVO.setCurrentStage(StageEnum.FINISH.getDesc());
            progressVO.setResultUrl(resultUrl);
            ProgressUtil.saveProgress(progressVO);
        }
    }
}
